package com.xiyou.weblog.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiyou.weblog.admin.model.vo.category.AddCategoryReqVO;
import com.xiyou.weblog.admin.model.vo.category.DeleteCategoryReqVO;
import com.xiyou.weblog.admin.model.vo.category.FindCategoryPageListReqVO;
import com.xiyou.weblog.admin.model.vo.category.FindCategoryPageListRspVO;
import com.xiyou.weblog.admin.service.AdminCategoryService;
import com.xiyou.weblog.common.domain.dos.ArticleCategoryRelDO;
import com.xiyou.weblog.common.domain.dos.CategoryDo;
import com.xiyou.weblog.common.domain.dos.TagDo;
import com.xiyou.weblog.common.domain.mapper.ArticleCategoryRelMapper;
import com.xiyou.weblog.common.domain.mapper.CategoryMapper;
import com.xiyou.weblog.common.enums.ResponseCodeEnum;
import com.xiyou.weblog.common.exception.BizException;
import com.xiyou.weblog.common.model.vo.SelectRspVO;
import com.xiyou.weblog.common.utils.PageResponse;
import com.xiyou.weblog.common.utils.Response;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.util.List;
import java.util.Objects;

/**
 * @author xiyou
 * @description 文章分类
 */
@Service
@Slf4j
public class AdminCategoryServiceImpl implements AdminCategoryService {

    @Resource
    CategoryMapper categoryMapper;
    @Resource
    ArticleCategoryRelMapper articleCategoryRelMapper;

    /**
     * 添加分类
     */
    @Override
    public Response addCategory(AddCategoryReqVO addCategoryReqVO) {
        String categoryName = addCategoryReqVO.getName();

        //先判断分类是否存在
        CategoryDo categoryDo = categoryMapper.selectByName(categoryName);

        if (Objects.nonNull(categoryDo)) {
            log.warn("分类名称： {}，此分类已存在", categoryName);
            throw new BizException(ResponseCodeEnum.CATEGORY_NAME_IS_EXISTED);
        }

        // 构建 Do 类
        CategoryDo insertCategoryDo = CategoryDo.builder()
                .name(addCategoryReqVO.getName().trim())
                .build();

        // 执行 insert
        categoryMapper.insert(insertCategoryDo);

        return Response.success();
    }


    /**
     * 分类分页数据查询
     */
    @Override
    public PageResponse findCategoryPageList(FindCategoryPageListReqVO findCategoryPageListReqVO) {
        // 获取当前页、以及每页需要展示的数据数量
        Long current = findCategoryPageListReqVO.getCurrent();
        Long size = findCategoryPageListReqVO.getSize();

        // 查询条件
        String name = findCategoryPageListReqVO.getName();
        LocalDate startDate = findCategoryPageListReqVO.getStartDate();
        LocalDate endDate = findCategoryPageListReqVO.getEndDate();

        // 分页查询
        Page<CategoryDo> page = categoryMapper.selectPageList(current, size, name, startDate, endDate);

        // 目标数据可能为空
        List<CategoryDo> categoryDos = page.getRecords();

        // DO 转 VO
        List<FindCategoryPageListRspVO> vos = null;
        if (!CollectionUtils.isEmpty(categoryDos)) {
            vos = categoryDos.stream()
                    .map(categoryDo -> FindCategoryPageListRspVO.builder()
                            .id(categoryDo.getId())
                            .name(categoryDo.getName())
                            .createTime(categoryDo.getCreateTime())
                            .build())
                    .toList();
        }

            /*
                上述代码解析：
                我们首先拿到了提交过来的查询页码、每页需要展示的数据数量两个字段，通过它们初始化了一个 Page 分页对象。然后，构建 SQL 的查询条件，包括当分类不为空时，添加名称的模糊查询；区间字段不为空时，构建 create_time 字段的大于等于、小于等于筛选，以及按创建时间倒叙排列。
                条件构建完毕后，通过调用 categoryMapper.selectPage() 方法执行分页查询，分页插件会自动帮助我们执行两条 SQL , select count(*) 用于查询记录总数，若有数据，则执行 limit 分页语句：
                查询执行完毕后，通过 getRecords() 方法拿到分页数据，若不为空，则通过 Java 8 的新特性 stream 流将 DO 类转换为 VO 视图类，返回给前端。
            */
        return PageResponse.success(page,vos);
    }

    /**
     * 删除分类
     */
    @Override
    public Response deleteCategory(DeleteCategoryReqVO deleteCategoryReqVO) {
        //分类 ID
        Long categoryId = deleteCategoryReqVO.getId();

        // 校验该分类下是否已经有文章，若有，则提示需要先删除分类下所有文章，才能删除
        ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectOneByCategoryId(categoryId);

        if (Objects.nonNull(articleCategoryRelDO)) {
            log.warn("==> 此分类下包含文章，无法删除，category: {}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_CAN_NOT_DELETE);
        }

        //删除分类
        categoryMapper.deleteById(categoryId);

        /*
        💡 TIP : 到这里，小伙伴们可能会疑惑，t_category 表中不是定义了一个逻辑删除字段吗？0 表示未删除，1 表示删除。逻辑删除应该是执行更新操作呀，将 is_deleted 字段更新成 1 才对。
            通常来说，在企业中，如果该条数据有价值，比如订单数据，用户可以主动去删除它，但并不是真的执行删除操作，而是通过逻辑删除字段将其标识为已删除，不是物理形式的从硬盘中抹去。因为订单数据很有价值，企业怎么可能轻易的丢弃它。通过它还可以计算用户画像，预测出你可能会对哪些商品感兴趣，下次再推送你感兴趣的商品给你，提供更好的服务给你（薅你羊毛）~
            而我们这里的分类数据，其实并没有什么价值，结合实际业务场景来分析，其实没有必要做逻辑删除，直接物理删除就好了。当初建表的时候，也是让大家知道，建表时正常情况下，需要添加哪些通用字段。
         */
        return Response.success();
    }

    /**
     * 获取文章分类的 Select 列表数据
     */
    @Override
    public Response findCategorySelectList() {
        //查询所欲分类
        List<CategoryDo> categoryDos = categoryMapper.selectList(null);

        //DO转VO
        List<SelectRspVO> selectRspVOS = null;
        // 如果分类数据不为空
        if (!CollectionUtils.isEmpty(categoryDos)) {
            // 将分类 ID 作为 Value 值，将分类名称作为 label 展示
            selectRspVOS = categoryDos.stream()
                    .map(categoryDo -> SelectRspVO.builder()
                            .label(categoryDo.getName())
                            .value(categoryDo.getId())
                            .build())
                    .toList();
        }

        return Response.success(selectRspVOS);
    }
}
